/*
 * Copyright (c) 2021 FuZhou LOCKZHINER Electronic Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

#include "ohos_init.h"
#include "los_task.h"
#include "e53_heart_rate_detection.h"
#include "lcd.h"
#include "picture.h"
#include "lcd_font.h"
#include "com_msg.h"
#include "e53_com.h"

#define MAX_BUFFER_SIZE                 500
#define EVENT_MASK                      0x00000001U

static char *str_state[] =
{
    "无效",
    "有效",
};

static lcd_param_t lcd = {.dir = 0, .f_color = LCD_LBBLUE, .b_color = LCD_WHITE, .f_size = 32, .start_line = IMAGE_H_SIEZ};
e53_hrd_data_t g_hrd_data = {0};
/***************************************************************
* 函数名称: lcd_reset
* 说    明: lcd显示复位
* 参    数: lcd_param_t lcd
* 返 回 值: 无
***************************************************************/
static void lcd_reset(lcd_param_t lcd)
{
    lcd_fill(0, 0, lcd.w, lcd.h, lcd.b_color);
    lcd_show_picture((lcd.w - IMAGE_W_SIEZ) / 2, 0, IMAGE_W_SIEZ, IMAGE_H_SIEZ, gImage_lingzhi);
}

/***************************************************************
* 函数名称: lcd_welcome
* 说    明: lcd显示欢迎界面
* 参    数: lcd_param_t lcd
* 返 回 值: 无
***************************************************************/
static void lcd_welcome(lcd_param_t lcd)
{
    lcd_reset(lcd);
    Printf_Lcd(0, lcd.start_line + lcd.f_size * 0, lcd.f_color, lcd.b_color, lcd.f_size, E_LCD_SCREEN_ALIGN_MID_NOP, "欢迎使用");
    int len = get_str_size("OpenHarmony", lcd.f_size);
    lcd_left_move(lcd.w - len, LCD_LEFT_MOVE_EDN(lcd.w, "OpenHarmony", lcd.f_size), lcd.start_line + lcd.f_size * 1, lcd.f_color, lcd.b_color, lcd.f_size, len, "OpenHarmony");
    lcd_right_move(0, LCD_RIGHT_MOVE_EDN(lcd.w, "智慧医疗", lcd.f_size), lcd.start_line + lcd.f_size * 2, lcd.f_color, lcd.b_color, lcd.f_size, "智慧医疗");
    LOS_Msleep(1000);
    lcd_reset(lcd);
}

static void send_data(void)
{
    msg_smart_medical_report_hrd_t hrd  = {0};
    e53_hrd_data_t                *data = &g_hrd_data;
    
    hrd.hrd   = data->hrd;
    hrd.hrd_v = data->hrd_flag;
    hrd.spo_v = data->spo2_flag;
    hrd.spo   = data->spo2;
    send_e53_data(0, &hrd);
}

/***************************************************************
* 函数名称: display_date
* 说    明: lcd显示数据
* 参    数: lcd_param_t lcd, e53_hrd_data_t data
* 返 回 值: 无
***************************************************************/
static void display_date(lcd_param_t lcd, e53_hrd_data_t data)
{
    g_hrd_data = data;
    send_data();
    // return;
#define H_SIZE 48
#define STR_TITLE   "心率:"
#define STR_TITLE1  "血氧:"
#define STR_PARAM   "%d"
#define STR_PARAM1  "%s"
#define STR_PARAM2  "------"
    uint16_t fc = lcd.f_color;
    static e53_hrd_data_t l = {0};
    
    if (data.hrd_flag == 2)
    {
        fc = LCD_RED;
    }
    else if (data.hrd_flag == 1)
    {
        fc = LCD_GREEN;
    }
    
    Printf_Lcd(0, lcd.start_line + lcd.f_size * 1, lcd.f_color, lcd.b_color, lcd.f_size, E_LCD_SCREEN_ALIGN_LEFT, STR_TITLE);
    Printf_Lcd(0, lcd.start_line + lcd.f_size * 2, lcd.f_color, lcd.b_color, lcd.f_size, E_LCD_SCREEN_ALIGN_LEFT, STR_TITLE1);
    if (data.hrd_flag != 0)
    {
        Printf_Lcd(get_str_size(STR_TITLE, lcd.f_size), lcd.start_line + lcd.f_size * 1, fc, lcd.b_color, lcd.f_size,
                   E_LCD_SCREEN_ALIGN_NONE_RIGHT_SURPLUS_NOP, STR_PARAM, data.hrd);
        l.hrd = data.hrd;
    }
    else
    {
        if (data.hrd == 0)
        {
            Printf_Lcd(get_str_size(STR_TITLE, lcd.f_size), lcd.start_line + lcd.f_size * 1, fc, lcd.b_color, lcd.f_size,
                       E_LCD_SCREEN_ALIGN_NONE_RIGHT_SURPLUS_NOP, STR_PARAM2);
        }
        else
        {
            Printf_Lcd(get_str_size(STR_TITLE, lcd.f_size), lcd.start_line + lcd.f_size * 1, fc, lcd.b_color, lcd.f_size,
                       E_LCD_SCREEN_ALIGN_NONE_RIGHT_SURPLUS_NOP, STR_PARAM, l.hrd);
        }
    }
    
    if (data.spo2_flag != 0)
    {
        Printf_Lcd(get_str_size(STR_TITLE1, lcd.f_size), lcd.start_line + lcd.f_size * 2, fc, lcd.b_color, lcd.f_size,
                   E_LCD_SCREEN_ALIGN_NONE_RIGHT_SURPLUS_NOP, STR_PARAM, data.spo2);
        l.spo2 = data.spo2;
    }
    else
    {
        if (data.hrd == 0)
        {
            Printf_Lcd(get_str_size(STR_TITLE, lcd.f_size), lcd.start_line + lcd.f_size * 2, fc, lcd.b_color, lcd.f_size,
                       E_LCD_SCREEN_ALIGN_NONE_RIGHT_SURPLUS_NOP, STR_PARAM2);
        }
        else
        {
            Printf_Lcd(get_str_size(STR_TITLE, lcd.f_size), lcd.start_line + lcd.f_size * 2, fc, lcd.b_color, lcd.f_size,
                       E_LCD_SCREEN_ALIGN_NONE_RIGHT_SURPLUS_NOP, STR_PARAM, l.spo2);
        }
    }
    
    if (data.hrd_flag > 0)
    {
        led_ht_set(OFF);
        lcd_show_picture(lcd.w - H_SIZE, lcd.start_line + lcd.f_size * 3, H_SIZE, H_SIZE, gImage_heart);
    }
    else
    {
        led_ht_set(ON);
        lcd_show_picture(lcd.w - H_SIZE, lcd.start_line + lcd.f_size * 3, H_SIZE, H_SIZE, gImage_ivheart);
    }
    
    if (data.press_flag > 0)
    {
        lcd_show_picture(lcd.w - H_SIZE * 2, lcd.start_line + lcd.f_size * 3, H_SIZE, H_SIZE, gImage_press);
    }
    else
    {
        lcd_show_picture(lcd.w - H_SIZE * 2, lcd.start_line + lcd.f_size * 3, H_SIZE, H_SIZE, gImage_nopress);
    }
    
    if (data.spo2_flag > 0)
    {
        led_bo_set(OFF);
        lcd_show_picture(lcd.w - H_SIZE, lcd.start_line + lcd.f_size * 3 + 48, H_SIZE, H_SIZE, gImage_spo2);
    }
    else
    {
        led_bo_set(ON);
        lcd_show_picture(lcd.w - H_SIZE, lcd.start_line + lcd.f_size * 3 + 48, H_SIZE, H_SIZE, gImage_ivspo2);
    }
    
    
    Printf_Lcd(0, lcd.start_line + lcd.f_size * 3, lcd.f_color, lcd.b_color, H_SIZE, E_LCD_SCREEN_ALIGN_LEFT, STR_TITLE);
    Printf_Lcd(0, lcd.start_line + lcd.f_size * 3 + 48, lcd.f_color, lcd.b_color, H_SIZE, E_LCD_SCREEN_ALIGN_LEFT, STR_TITLE1);
    Printf_Lcd(get_str_size(STR_TITLE1, H_SIZE), lcd.start_line + lcd.f_size * 3, fc, lcd.b_color, H_SIZE,
               E_LCD_SCREEN_ALIGN_NONE, STR_PARAM1, str_state[data.hrd_flag > 0 ? 1 : 0]);
    Printf_Lcd(get_str_size(STR_TITLE1, H_SIZE), lcd.start_line + lcd.f_size * 3 + 48, fc, lcd.b_color, H_SIZE,
               E_LCD_SCREEN_ALIGN_NONE, STR_PARAM1, str_state[data.spo2_flag > 0 ? 1 : 0]);
}

/***************************************************************
* 函数名称: display_init
* 说    明: lcd显示初始化
* 参    数: lcd_param_t *lcd, char *name
* 返 回 值: 无
***************************************************************/
static void display_init(lcd_param_t *lcd, char *name)
{
    printf("\n\n%s\n", __FUNCTION__);
    lcd->w = g_lcd_size.w;
    lcd->h = g_lcd_size.h;
    lcd_welcome(*lcd);
    Printf_Lcd(0, lcd->start_line + lcd->f_size * 0, lcd->f_color, lcd->b_color, lcd->f_size, E_LCD_SCREEN_ALIGN_MID_NOP, name);
}

//平滑滤波
static void calc_smooth(uint32_t *buffer, uint32_t buffer_len)
{
#define SMOOTH_LENGTH           10
    uint32_t smooth_res = 0;
    uint32_t smooth_middle = (SMOOTH_LENGTH % 2 == 0) ? (SMOOTH_LENGTH / 2) : (SMOOTH_LENGTH / 2 + 1);
    
    for (uint32_t i = 0; i < SMOOTH_LENGTH; i++)
    {
        smooth_res += buffer[i];
    }
    
    for (uint32_t i = SMOOTH_LENGTH; i < buffer_len; i++)
    {
        smooth_res -= buffer[i - SMOOTH_LENGTH];
        smooth_res += buffer[i];
        buffer[i - smooth_middle] = smooth_res / SMOOTH_LENGTH;
    }
}


//有无手指按压检测 返回1 有手指按压， 返回0 无手指按压
static int get_finger_press(uint32_t *buf, uint32_t len)
{
    int i = 0, n = 0;
    uint32_t sum = 0;
    
    for (i = 0; i < len; ++i)
    {
        if (*buf < 262144)
        {
            sum += *(buf + i);
            n++;
        }
    }
    
    if (n == 0)
    {
        return 0;
    }
    sum /= n;
    printf("hrdadc:%u\n", sum);
    return (sum > 60000) ? 1 : 0;
}

//快速排序比较大小
static int qcomp(const void *a, const void *b)
{
    return *(uint32_t*)a > *(uint32_t*)b ? 1 : -1;
}

#define MAX_BUFFER_SIZE                 500
#define EVENT_MASK                      0x00000001U

/***************************************************************
* 函数名称: e53_hrd_thread
* 说    明: 智慧医疗例程
* 参    数: 无
* 返 回 值: 无
***************************************************************/
void e53_hrd_thread(void *args)
{
    uint8_t buffer[6];
    uint32_t i = 0;
    uint32_t buffer_ir[MAX_BUFFER_SIZE];//IR LED sensor data
    uint32_t buffer_red[MAX_BUFFER_SIZE];//Red LED sensor data
    int32_t spo2 = 0;         //SPO2 value
    int8_t spo2_valid = 0;    //indicator to show if the SP02 calculation is valid
    int32_t heart_rate = 0;   //heart rate value
    int8_t  hr_valid = 0;     //indicator to show if the heart rate calculation is valid
    uint32_t ir_cnt = 0, ir_err = 0, ir_range = 0, frist_ir = 1, ir_valid = 0;
    uint8_t  finger_press  = 0;
    uint32_t ir[10] = {0}, ir_invalid[10] = {0};
    e53_hrd_data_t data = {0};
    static int init = 0;
    
    if (init == 0)
    {
        display_init(&lcd, "智慧医疗");
        init = 1;
    }
    else
    {
        lcd_reset(lcd);
        Printf_Lcd(0, lcd.start_line + lcd.f_size * 0, lcd.f_color, lcd.b_color, lcd.f_size, E_LCD_SCREEN_ALIGN_MID_NOP, "智慧医疗");
    }
    display_date(lcd, data);
    e53_hrd_init();
    
    memset(buffer_ir, 0, MAX_BUFFER_SIZE);
    memset(buffer_red, 0, MAX_BUFFER_SIZE);
    printf("%s\n", __FUNCTION__);
    
    while (1)
    {
        while (i < MAX_BUFFER_SIZE)
        {
            uint8_t fifo_maxsize = 32;
            uint8_t fifo_wr_ptr = Max30102_I2c_Read(REG_FIFO_WR_PTR);
            uint8_t fifo_flow_ptr = Max30102_I2c_Read(REG_OVF_COUNTER);
            uint8_t fifo_rd_ptr = Max30102_I2c_Read(REG_FIFO_RD_PTR);
            uint8_t fifo_valid = (fifo_wr_ptr + fifo_maxsize - fifo_rd_ptr) % fifo_maxsize;
            
            
            if (fifo_valid  == 0)
            {
                if (fifo_flow_ptr)
                {
                    uint8_t v[6];
                    printf("%s fifo_flow_ptr %d,%d\r\n", __FUNCTION__, fifo_wr_ptr, fifo_rd_ptr);
                    for (uint8_t fifo_i = 0; fifo_i < fifo_maxsize; fifo_i++)
                    {
                        if (Max30102_I2c_ReadBuffer(REG_FIFO_DATA, v, 6))
                        {
                            break;
                        }
                    }
                }
                LOS_Msleep(10);
                continue;
            }
            
            
            for (uint8_t fifo_i = 0; fifo_i < fifo_valid; fifo_i++)
            {
                if (Max30102_I2c_ReadBuffer(REG_FIFO_DATA, buffer, 6))
                {
                    break;
                }
                /*Combine values to get the actual number*/
                buffer_red[i] = (uint32_t)((buffer[0] & 0x03) << 16);
                buffer_red[i] |= (uint32_t)(buffer[1] << 8);
                buffer_red[i] |= (uint32_t)(buffer[2] << 0);
                buffer_ir[i] = (uint32_t)((buffer[3] & 0x03) << 16);
                buffer_ir[i] |= (uint32_t)(buffer[4] << 8);
                buffer_ir[i] |= (uint32_t)(buffer[5] << 0);
                
                i++;
                if (i >= MAX_BUFFER_SIZE)
                {
                    break;
                }
            }
        }
        
        if (get_finger_press(&buffer_ir[440], 50) == 0)
        {
            data.press_flag = 0;
            // printf("no finger press\n");
            i = 0;
            frist_ir = 1;
            finger_press = 0;
            goto irerr;
        }
        data.press_flag = 1;
        
        if (finger_press == 0)
        {
            finger_press = 1;
            memmove(buffer_ir, &buffer_ir[100], 400 * 4);
            memmove(buffer_red, &buffer_red[100], 400 * 4);
            i = 400;
            display_date(lcd, data);
            printf("finger press\n");
            continue;
        }
        
        calc_smooth(buffer_ir, MAX_BUFFER_SIZE);
        calc_smooth(buffer_red, MAX_BUFFER_SIZE);
        maxim_heart_rate_and_oxygen_saturation(buffer_ir, MAX_BUFFER_SIZE, buffer_red, &spo2, &spo2_valid, &heart_rate, &hr_valid);
        
        memmove(buffer_ir, &buffer_ir[100], 400 * 4);
        memmove(buffer_red, &buffer_red[100], 400 * 4);
        
        i = 400;
        printf("hr1:%ld\n", heart_rate);
        
        if (heart_rate == -999 || heart_rate == 666)
        {
            goto irerr;
        }
        
#define AVERAGE_CNT_MIN  5    //最少求平均值次数
#define RANGE_MAX        20   //差异值允许范围
        
        if (heart_rate > 45 && heart_rate < 180)
        {
            static uint8_t invalid_cnt = 0;
            if (frist_ir == 1 || (ir_valid != 0 && abs(ir_valid - heart_rate) > RANGE_MAX)) //差值过大的过滤掉
            {
                frist_ir = 0;
                if ((ir_valid != 0 && abs(ir_valid - heart_rate) > RANGE_MAX))          //防止ir_valid 是无效数据导致一直被过滤
                {
                    static int32_t h_1 = 0, h_2 = 0;
                    ir_invalid[++invalid_cnt % AVERAGE_CNT_MIN] = heart_rate;
                    if (invalid_cnt % (AVERAGE_CNT_MIN * 2) != 0 && invalid_cnt % AVERAGE_CNT_MIN == 0)
                    {
                        //求第一轮无效值平均值
                        
                        qsort(ir_invalid, AVERAGE_CNT_MIN, 4, qcomp);
                        h_1 = 0;
                        for (int ih_5 = 1; ih_5 < AVERAGE_CNT_MIN - 1; ++ih_5)
                        {
                            h_1 += ir_invalid[ih_5];
                        }
                        h_1 /= AVERAGE_CNT_MIN - 2;
                    }
                    else if (invalid_cnt % (AVERAGE_CNT_MIN * 2) == 0)
                    {
                        //求第二轮无效值平均值
                        qsort(ir_invalid, AVERAGE_CNT_MIN, 4, qcomp);
                        h_2 = 0;
                        for (int ih_10 = 1; ih_10 < AVERAGE_CNT_MIN - 1; ++ih_10)
                        {
                            h_2 += ir_invalid[ih_10];
                        }
                        h_2 /= AVERAGE_CNT_MIN - 2;
                        if (abs(h_2 - h_1) < RANGE_MAX)         // ir_valid 误差过大校正
                        {
                            ir_valid = (h_2 + h_1) / 2;
                            for (int ircnt = 0; ircnt < AVERAGE_CNT_MIN + ir_range; ++ircnt)
                            {
                                ir[ircnt] = ir_valid;
                            }
                            printf("adjust ir_valid:%lu\n", ir_valid);
                        }
                    }
                    printf("ir_invalid:%lu,%lu,%u\n", ir_valid, heart_rate, abs(ir_valid - heart_rate));
                }
                continue;
            }
            
            invalid_cnt = 0;
            ir_err = 0;
            ir[ir_cnt % (AVERAGE_CNT_MIN + ir_range)] = heart_rate;
            if (++ir_cnt >= AVERAGE_CNT_MIN)
            {
                // ir_valid = 0;
                // for(int ircnt = 0; ircnt < AVERAGE_CNT_MIN+ir_range; ++ircnt) //动态限幅平均值（5-10个平均值）
                // {
                //     ir_valid += ir[ircnt];
                // }
                // ir_valid /= (AVERAGE_CNT_MIN+ir_range);
                
                qsort(ir, AVERAGE_CNT_MIN + ir_range, 4, qcomp);
                ir_valid = 0;
                for (int ircnt = 1; ircnt < AVERAGE_CNT_MIN + ir_range - 2; ++ircnt) //去掉最大值和最小值再取平均
                {
                    ir_valid += ir[ircnt];
                    printf("ave:%lu\n", ir[ircnt]);
                }
                ir_valid /= (AVERAGE_CNT_MIN + ir_range) - 3;
                
                ir_range = ir_range < AVERAGE_CNT_MIN ? ir_range + 1 : ir_range;
                printf("spo2:%ld %s heart rate:%ld %s\n\n", spo2, spo2_valid ? "valid" : "invalid", ir_valid, hr_valid ? "valid" : "invalid");
                data.hrd       = ir_valid;
                data.hrd_flag  = hr_valid;
                data.spo2      = spo2;
                data.spo2_flag = spo2_valid;
                display_date(lcd, data);
            }
        }
        else if (ir_err != 0)
        {
irerr:
            frist_ir   = 1;
            ir_cnt     = 0;
            ir_range   = 0;
            ir_valid   = 0;
            
            heart_rate = 0;
            spo2       = 0;
            hr_valid   = 0;
            spo2_valid = 0;
            data.hrd       = heart_rate;
            data.hrd_flag  = hr_valid;
            data.spo2      = spo2;
            data.spo2_flag = spo2_valid;
            display_date(lcd, data);
        }
    }
}

void e53_hrd_example()
{
    unsigned int ret = LOS_OK;
    unsigned int thread_id;
    TSK_INIT_PARAM_S task = {0};
    
    task.pfnTaskEntry = (TSK_ENTRY_FUNC)e53_hrd_thread;
    task.uwStackSize = 10240;
    task.pcName = "e53_hrd_thread";
    task.usTaskPrio = 2;
    ret = LOS_TaskCreate(&thread_id, &task);
    if (ret != LOS_OK)
    {
        printf("Falied to create e53_hrd_thread ret:0x%x\n", ret);
        return;
    }
}

// APP_FEATURE_INIT(e53_hrd_example);

